#include <stdio.h>
#include <windows.h>
#include <time.h>
#include "camera.h"
#include "pixelconv.h"


static camera_t cam_;
static uint32_t width_;
static uint32_t height_;
static uint8_t* buffer_;
static char filename_[256];
static CRITICAL_SECTION sec_;
static HWND main_;
static int gray_; //灰化
static int reverse_; //反色


void show_help(const char* name){
	printf("Usage: %s [--gray] [--reverse] [file.bmp]\n", name);
	exit(1);
}

static void convert_pixel(const void* pixel, void* out){	
	if(gray_){
		pixel_yuy2_to_gray_rgb(pixel, buffer_, width_, height_);
	}else{
		pixel_yuy2_to_bgr(pixel, buffer_, width_, height_);
	}
	if(reverse_){
		pixel_rgb_reverse(buffer_, buffer_, width_, height_);
	}
}

static int init_camera(){
	uint32_t type;
	char types[6] = {0};
	cam_ = camera_open(0, CAMERA_MAX_RESOLUTION);
	if(!cam_){
		printf("open camera fail\n");
		return -1;
	}
	width_ = camera_get_width(cam_);
	height_ = camera_get_height(cam_);
	type = camera_get_type(cam_); 	
	memcpy(types, &type, 4);
	printf("camera info %dx%d %s\n", width_, height_, types);
	return 0;
}


//保存BMP
int save_bmp(const char * bmpFile, uint8_t* pRgbData, int width, int height){
	int size = 0;
	int bitsPerPixel = 3;	
	size = width * height * bitsPerPixel;
 
	BITMAPFILEHEADER bfh;
	bfh.bfType = (WORD)0x4d42;  
	bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	bfh.bfSize = size + bfh.bfOffBits;
	bfh.bfReserved1 = 0;
	bfh.bfReserved2 = 0;
 
	BITMAPINFOHEADER bih;
	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biWidth = width;
	bih.biHeight = -height;	
	bih.biPlanes = 1;
	bih.biBitCount = 24;
	bih.biCompression = BI_RGB;
	bih.biSizeImage = size;
	bih.biXPelsPerMeter = 0;
	bih.biYPelsPerMeter = 0;
	bih.biClrUsed = 0;
	bih.biClrImportant = 0;

	FILE* fp = fopen(bmpFile, "wb");
	if(!fp) return -1;
 
	fwrite(&bfh, 1, 8, fp);
	fwrite(&bfh.bfReserved2, 1, sizeof(bfh.bfReserved2),  fp);
	fwrite(&bfh.bfOffBits, 1, sizeof(bfh.bfOffBits), fp);
	fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);
	fwrite(pRgbData, size, 1, fp);
	fclose(fp);
	return 0;
}

//直接拍照回调
void direct_take_image_cb(const void* pixel, int len, void* udata){
	int ret;
	convert_pixel(pixel, buffer_);
	ret = save_bmp(filename_, buffer_, width_, height_);
	if(ret){
		printf("write image file error\n");
	}else{
		printf("OK\n");
	}
	exit(ret);
}

//直接拍照
int do_take_photo(const char* savename){
	snprintf(filename_, 256, "%s", savename);	
	if(init_camera())return -1;	
	buffer_ = (uint8_t*)malloc(width_ * height_ * 4);
	if(!buffer_){
		printf("out of memory\n");
		return -1;
	}
	camera_set_callback(cam_, direct_take_image_cb, NULL);
	Sleep(5000);
	printf("read camera timeout\n");
	return -1;
}

/*
BITMAPINFO* make_test_data(){
	int x,y;
	static BITMAPINFO* bi;
	if(!bi){
		bi = (BITMAPINFO*)malloc(sizeof(BITMAPINFO) + 256*sizeof(RGBQUAD));
	memset(bi, 0, sizeof(BITMAPINFOHEADER));
    bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bi->bmiHeader.biPlanes = 1;
    bi->bmiHeader.biWidth = width_;
    bi->bmiHeader.biHeight = 0 - height_;
    bi->bmiHeader.biBitCount = 24;
    bi->bmiHeader.biCompression = BI_RGB; 
	bi->bmiHeader.biClrUsed = 256;
	RGBQUAD *rgb = (RGBQUAD*)((BYTE*)bi + sizeof(BITMAPINFOHEADER));
	for (int i = 0; i < 256; ++i) {
		rgb[i].rgbBlue = rgb[i].rgbGreen = rgb[i].rgbRed = i;
	}
	}
	uint8_t* ptr = buffer_;
	bi->bmiHeader.biBitCount = 8;
    for(y = 0;y<height_;y++){
        for(x = 0;x<width_;x++){
            if(x<(width_/2)){
                if(y<(height_)/2){
                    ptr[0] = 0xE0;
                }else{
                    ptr[0] = 0x20;
                }
            }else {
                ptr[0] = 0x80;
            }
            ptr += 1;
        }
    }

	return bi;
}
*/

//绘制窗口
VOID MainWndOnPaint(HWND hwnd){    
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);      
    BITMAPINFO bi = {0};
    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bi.bmiHeader.biPlanes = 1;
    bi.bmiHeader.biWidth = width_;
    bi.bmiHeader.biHeight = 0 - height_;
    bi.bmiHeader.biBitCount = 24;
    bi.bmiHeader.biCompression = BI_RGB;    
    EnterCriticalSection(&sec_);	
    StretchDIBits(hdc, 0, 0, width_,height_, 0,0,width_,height_, buffer_, &bi, DIB_RGB_COLORS, SRCCOPY);
    LeaveCriticalSection(&sec_);
    EndPaint(hwnd, &ps);    
}


void take(){
	time_t t = 0;
	time(&t);    
    struct tm* tptr = localtime(&t);    
	char filename[128];
    strftime(filename,128,"PHOTO_%Y%m%d%H%M%S.bmp", tptr);
	int ret = save_bmp(filename, buffer_, width_, height_);
	if(ret){
		printf("write image file error\n");
	}else{
		printf("%s\n", filename);
		printf("save ok\n");
	}
	PostQuitMessage(ret);
}


//主窗口过程
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
    switch (message){              
        case WM_DESTROY:
            PostQuitMessage(1);
            break;
        case WM_LBUTTONUP:
            take();
            break;
        case WM_PAINT:
            MainWndOnPaint(hwnd);
            break;
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 1;
}

static void on_data(const void* buf, int buflen, void* udata){
    EnterCriticalSection(&sec_);
    //memcpy(pixel_, buf, buflen);
    //pixel_yuy2_to_bgr(buf, buffer_, width_, height_);
	convert_pixel(buf, buffer_);
    InvalidateRect(main_, NULL, FALSE);
    LeaveCriticalSection(&sec_);
}

//注册主窗口类
BOOL RegisterWindowClass(){
    WNDCLASS wc = {0};
    wc.style = CS_VREDRAW|CS_HREDRAW;    
    wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpfnWndProc = MainWndProc;    
    wc.lpszClassName = TEXT("MainWindowClass");
    return RegisterClass(&wc);
}

int show_window(){
	InitializeCriticalSection(&sec_) ;
	if(init_camera())return -1;	
	buffer_ = (uint8_t*)malloc(width_ * height_ * 4);
	if(!buffer_){
		printf("out of memory\n");
		return -1;
	}
	camera_set_callback(cam_, on_data, NULL);	
	RegisterWindowClass();

	RECT rc;
	rc.left = 0; rc.top = 0;
	rc.right = width_; rc.bottom = height_;
	UINT32 style = (WS_OVERLAPPED|WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU);
	UINT32 styleex = 0;
	AdjustWindowRectEx(&rc, style, FALSE, styleex);
	HWND hwnd = CreateWindowEx(styleex, TEXT("MainWindowClass"),TEXT("Camera"),
        style, 
        CW_USEDEFAULT,CW_USEDEFAULT,rc.right,rc.bottom,NULL,NULL,NULL,NULL);	
	ShowWindow(hwnd, SW_SHOW);  
	main_ = hwnd;  
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;	
}


int main(int argc, char* argv[]){
	char* opt;
	int i;
	for(i = 1; i < argc; i++){		
		opt = argv[i];
		if(strcmp(opt, "--help") == 0){
			show_help(argv[0]);
		}else if(strcmp(opt,"--gray")==0){
			gray_ = 1;
		}else if(strcmp(opt,"--reverse")==0){
			reverse_ = 1;
		}else if(opt[0] == '-'){
			printf("unknown option %s\n", opt);
			show_help(argv[0]);
		}else{
			return do_take_photo(opt);
		}
	}
	return show_window();
}
